/*     */ package com.zimbra.cs.service.mail;
/*     */ 
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.soap.Element;
/*     */ import com.zimbra.common.soap.MailConstants;
/*     */ import com.zimbra.common.util.Pair;
/*     */ import com.zimbra.common.util.StringUtil;
/*     */ import com.zimbra.cs.mailbox.Flag;
/*     */ import com.zimbra.cs.mailbox.Folder;
/*     */ import com.zimbra.cs.mailbox.MailItem;
/*     */ import com.zimbra.cs.mailbox.MailItem.Type;
/*     */ import com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException;
/*     */ import com.zimbra.cs.mailbox.Mailbox;
/*     */ import com.zimbra.cs.mailbox.Mailbox.FolderNode;
/*     */ import com.zimbra.cs.mailbox.MailboxLock;
/*     */ import com.zimbra.cs.mailbox.OperationContext;
/*     */ import com.zimbra.cs.mailbox.OperationContextData;
/*     */ import com.zimbra.cs.mailbox.Tag;
/*     */ import com.zimbra.cs.mailbox.util.TypedIdList;
/*     */ import com.zimbra.cs.mailbox.util.TypedIdList.ItemInfo;
/*     */ import com.zimbra.cs.service.util.ItemId;
/*     */ import com.zimbra.cs.service.util.ItemIdFormatter;
/*     */ import com.zimbra.soap.ZimbraSoapContext;
/*     */ import java.util.Collections;
/*     */ import java.util.EnumSet;
/*     */ import java.util.HashSet;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ import java.util.Map.Entry;
/*     */ import java.util.Set;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class Sync
/*     */   extends MailDocumentHandler
/*     */ {
/*  51 */   protected static final String[] TARGET_FOLDER_PATH = { "l" };
/*  52 */   protected String[] getProxiedIdPath(Element request) { return TARGET_FOLDER_PATH; }
/*     */   
/*     */   private static final int DEFAULT_FOLDER_ID = 11;
/*     */   public Element handle(Element request, Map<String, Object> context) throws ServiceException {
/*  56 */     ZimbraSoapContext zsc = getZimbraSoapContext(context);
/*  57 */     Mailbox mbox = getRequestedMailbox(zsc);
/*  58 */     OperationContext octxt = getOperationContext(zsc, context);
/*  59 */     ItemIdFormatter ifmt = new ItemIdFormatter(zsc);
/*     */     
/*  61 */     String token = request.getAttribute("token", "0");
/*     */     
/*  63 */     Element response = zsc.createElement(MailConstants.SYNC_RESPONSE);
/*  64 */     response.addAttribute("md", System.currentTimeMillis() / 1000L);
/*     */     
/*     */     int tokenInt;
/*     */     int itemCutoff;
/*     */     try
/*     */     {
/*  70 */       int delimiter = token.indexOf('-');
/*  71 */       int itemCutoff; if (delimiter < 1) {
/*  72 */         int tokenInt = Integer.parseInt(token);
/*  73 */         itemCutoff = -1;
/*     */       } else {
/*  75 */         tokenInt = Integer.parseInt(token.substring(0, delimiter));
/*  76 */         itemCutoff = Integer.parseInt(token.substring(delimiter + 1));
/*     */       }
/*     */     } catch (NumberFormatException nfe) {
/*  79 */       throw ServiceException.INVALID_REQUEST("malformed sync token: " + token, nfe);
/*     */     }
/*  81 */     boolean initialSync = tokenInt <= 0;
/*     */     
/*     */ 
/*  84 */     long calendarStart = request.getAttributeLong("calCutoff", -1L);
/*  85 */     long messageSyncStart = request.getAttributeLong("msgCutoff", -1L);
/*     */     
/*     */ 
/*  88 */     Folder root = null;
/*  89 */     ItemId iidFolder = null;
/*     */     try {
/*  91 */       iidFolder = new ItemId(request.getAttribute("l", "11"), zsc);
/*  92 */       OperationContext octxtOwner = new OperationContext(mbox);
/*  93 */       root = mbox.getFolderById(octxtOwner, iidFolder.getId());
/*     */     }
/*     */     catch (MailServiceException.NoSuchItemException nsie) {}
/*  96 */     Set<Folder> visible = octxt.isDelegatedRequest(mbox) ? mbox.getVisibleFolders(octxt) : null;
/*     */     
/*  98 */     Mailbox.FolderNode rootNode = null;
/*  99 */     if ((root == null) || (iidFolder == null))
/*     */     {
/* 101 */       rootNode = mbox.getFolderTree(octxt, null, true);
/*     */     }
/*     */     else {
/* 104 */       rootNode = mbox.getFolderTree(octxt, iidFolder, true);
/*     */     }
/* 106 */     OperationContextData.addGranteeNames(octxt, rootNode);
/*     */     
/*     */ 
/* 109 */     mbox.lock.lock();
/*     */     try {
/* 111 */       mbox.beginTrackingSync();
/*     */       
/* 113 */       if (initialSync) {
/* 114 */         response.addAttribute("token", mbox.getLastChangeID());
/* 115 */         response.addAttribute("s", mbox.getSize());
/*     */         
/* 117 */         boolean anyFolders = folderSync(response, octxt, ifmt, mbox, root, visible, calendarStart, messageSyncStart, SyncPhase.INITIAL);
/*     */         
/* 119 */         if (!anyFolders) {
/* 120 */           response.addElement("folder");
/*     */         }
/*     */       } else {
/* 123 */         boolean typedDeletes = request.getAttributeBool("typed", false);
/* 124 */         String newToken = deltaSync(response, octxt, ifmt, mbox, tokenInt, itemCutoff, typedDeletes, root, visible, messageSyncStart);
/* 125 */         response.addAttribute("token", newToken);
/*     */       }
/*     */     } finally {
/* 128 */       mbox.lock.release();
/*     */     }
/*     */     
/* 131 */     return response;
/*     */   }
/*     */   
/*     */   private static enum SyncPhase {
/* 135 */     INITIAL,  DELTA;
/*     */     
/*     */     private SyncPhase() {}
/*     */   }
/*     */   
/*     */   private static boolean folderSync(Element response, OperationContext octxt, ItemIdFormatter ifmt, Mailbox mbox, Folder folder, Set<Folder> visible, long calendarStart, long messageSyncStart, SyncPhase phase)
/*     */     throws ServiceException
/*     */   {
/* 143 */     if (folder == null)
/* 144 */       return false;
/* 145 */     if ((visible != null) && (visible.isEmpty()))
/* 146 */       return false;
/* 147 */     boolean isVisible = (visible == null) || (visible.remove(folder));
/*     */     
/*     */ 
/* 150 */     List<Folder> subfolders = folder.getSubfolders(null);
/* 151 */     if ((!isVisible) && (subfolders.isEmpty())) {
/* 152 */       return false;
/*     */     }
/*     */     
/* 155 */     boolean initial = phase == SyncPhase.INITIAL;
/* 156 */     Element f = ToXML.encodeFolder(response, ifmt, octxt, folder, -1);
/* 157 */     if ((initial) && (isVisible) && (folder.getType() == MailItem.Type.FOLDER))
/*     */     {
/* 159 */       if (folder.getId() == 8) {
/* 160 */         initialTagSync(f, octxt, ifmt, mbox);
/*     */       } else {
/* 162 */         TypedIdList idlist = mbox.getItemIds(octxt, folder.getId());
/* 163 */         initialMsgSync(f, idlist, octxt, mbox, folder, messageSyncStart);
/* 164 */         initialItemSync(f, "chat", idlist.getIds(MailItem.Type.CHAT));
/* 165 */         initialItemSync(f, "cn", idlist.getIds(MailItem.Type.CONTACT));
/* 166 */         initialItemSync(f, "note", idlist.getIds(MailItem.Type.NOTE));
/* 167 */         initialCalendarSync(f, idlist, octxt, mbox, folder, calendarStart);
/* 168 */         initialItemSync(f, "doc", idlist.getIds(MailItem.Type.DOCUMENT));
/* 169 */         initialItemSync(f, "w", idlist.getIds(MailItem.Type.WIKI));
/* 170 */         initialItemSync(f, "c", idlist.getIds(MailItem.Type.CONVERSATION));
/*     */       }
/*     */     }
/*     */     
/* 174 */     if ((isVisible) && (visible != null) && (visible.isEmpty())) {
/* 175 */       return true;
/*     */     }
/*     */     
/* 178 */     for (Folder subfolder : subfolders) {
/* 179 */       if (subfolder != null) {
/* 180 */         isVisible |= folderSync(f, octxt, ifmt, mbox, subfolder, visible, calendarStart, messageSyncStart, phase);
/*     */       }
/*     */     }
/*     */     
/*     */ 
/* 185 */     if (!isVisible) {
/* 186 */       f.detach();
/*     */     }
/*     */     
/* 189 */     return isVisible;
/*     */   }
/*     */   
/*     */   private static void initialMsgSync(Element f, TypedIdList idlist, OperationContext octxt, Mailbox mbox, Folder folder, long messageSyncStart) throws ServiceException {
/* 193 */     if ((messageSyncStart > 0L) && (!Collections.disjoint(idlist.types(), EnumSet.of(MailItem.Type.MESSAGE)))) {
/* 194 */       idlist = mbox.listItemsForSync(octxt, folder.getId(), MailItem.Type.MESSAGE, messageSyncStart);
/*     */     }
/*     */     
/* 197 */     initialItemSync(f, "m", idlist.getIds(MailItem.Type.MESSAGE));
/*     */   }
/*     */   
/*     */   private static void initialTagSync(Element f, OperationContext octxt, ItemIdFormatter ifmt, Mailbox mbox) throws ServiceException {
/* 201 */     for (Tag tag : mbox.getTagList(octxt)) {
/* 202 */       if ((tag != null) && (!(tag instanceof Flag))) {
/* 203 */         ToXML.encodeTag(f, ifmt, octxt, tag, -1);
/*     */       }
/*     */     }
/*     */   }
/*     */   
/* 208 */   private static final Set<MailItem.Type> CALENDAR_TYPES = EnumSet.of(MailItem.Type.APPOINTMENT, MailItem.Type.TASK);
/*     */   private static final int FETCH_BATCH_SIZE = 200;
/*     */   private static final int MAXIMUM_CHANGE_COUNT = 3990;
/*     */   private static final int MUTABLE_FIELDS = 4223782;
/*     */   
/*     */   private static void initialCalendarSync(Element f, TypedIdList idlist, OperationContext octxt, Mailbox mbox, Folder folder, long calendarStart)
/*     */     throws ServiceException
/*     */   {
/* 216 */     if ((calendarStart > 0L) && (!Collections.disjoint(idlist.types(), CALENDAR_TYPES))) {
/* 217 */       idlist = mbox.listCalendarItemsForRange(octxt, MailItem.Type.UNKNOWN, calendarStart, -1L, folder.getId());
/*     */     }
/* 219 */     initialItemSync(f, "appt", idlist.getIds(MailItem.Type.APPOINTMENT));
/* 220 */     initialItemSync(f, "task", idlist.getIds(MailItem.Type.TASK));
/*     */   }
/*     */   
/*     */   private static void initialItemSync(Element f, String ename, List<Integer> items) {
/* 224 */     if ((items == null) || (items.isEmpty()))
/* 225 */       return;
/* 226 */     f.addElement(ename).addAttribute("ids", StringUtil.join(",", items));
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 235 */   private static final Set<MailItem.Type> FOLDER_TYPES = EnumSet.of(MailItem.Type.FOLDER, MailItem.Type.SEARCHFOLDER, MailItem.Type.MOUNTPOINT);
/*     */   
/*     */   private static String deltaSync(Element response, OperationContext octxt, ItemIdFormatter ifmt, Mailbox mbox, int begin, int itemCutoff, boolean typedDeletes, Folder root, Set<Folder> visible, long messageSyncStart)
/*     */     throws ServiceException
/*     */   {
/* 240 */     String newToken = mbox.getLastChangeID() + "";
/* 241 */     if (begin >= mbox.getLastChangeID()) {
/* 242 */       return newToken;
/*     */     }
/*     */     
/* 245 */     TypedIdList tombstones = mbox.getTombstones(begin);
/* 246 */     Element eDeleted = response.addElement("deleted");
/*     */     
/*     */ 
/* 249 */     List<Folder> hierarchy = (root == null) || (root.getId() == 1) ? null : root.getSubfolderHierarchy();
/* 250 */     Set<Integer> targetIds = (root != null) && (root.getId() == 1) ? null : new HashSet(hierarchy == null ? 0 : hierarchy.size());
/* 251 */     if (hierarchy != null) {
/* 252 */       for (Folder folder : hierarchy) {
/* 253 */         targetIds.add(Integer.valueOf(folder.getId()));
/*     */       }
/*     */     }
/*     */     
/*     */ 
/* 258 */     if (octxt.isDelegatedRequest(mbox))
/*     */     {
/* 260 */       if ((!mbox.getModifiedFolders(begin).isEmpty()) || (!Collections.disjoint(tombstones.types(), FOLDER_TYPES)))
/*     */       {
/* 262 */         boolean anyFolders = folderSync(response, octxt, ifmt, mbox, root, visible, -1L, messageSyncStart, SyncPhase.DELTA);
/*     */         
/* 264 */         if (!anyFolders) {
/* 265 */           response.addElement("folder");
/*     */         }
/*     */       }
/*     */     } else {
/* 269 */       for (Folder folder : mbox.getModifiedFolders(begin))
/*     */       {
/*     */ 
/* 272 */         if ((targetIds == null) || (targetIds.contains(Integer.valueOf(folder.getId())))) {
/* 273 */           ToXML.encodeFolder(response, ifmt, octxt, folder, -1);
/*     */         } else {
/* 275 */           tombstones.add(folder.getType(), Integer.valueOf(folder.getId()), folder.getUuid());
/*     */         }
/*     */       }
/*     */     }
/*     */     
/*     */ 
/* 281 */     for (Tag tag : mbox.getModifiedTags(octxt, begin)) {
/* 282 */       ToXML.encodeTag(response, ifmt, octxt, tag, -1);
/*     */     }
/*     */     
/*     */ 
/* 286 */     int itemCount = 0;
/* 287 */     Pair<List<Integer>, TypedIdList> changed = mbox.getModifiedItems(octxt, begin, MailItem.Type.UNKNOWN, targetIds);
/* 288 */     List<Integer> modified = (List)changed.getFirst();
/* 289 */     while (!modified.isEmpty()) {
/* 290 */       List<Integer> batch = modified.subList(0, Math.min(modified.size(), 200));
/* 291 */       for (MailItem item : mbox.getItemById(octxt, batch, MailItem.Type.UNKNOWN))
/*     */       {
/* 293 */         if ((item.getModifiedSequence() != begin + 1) || (item.getId() >= itemCutoff))
/*     */         {
/*     */ 
/*     */ 
/* 297 */           if (itemCount >= 3990) {
/* 298 */             response.addAttribute("more", true);
/* 299 */             newToken = item.getModifiedSequence() - 1 + "-" + item.getId();
/*     */             
/*     */ 
/*     */ 
/*     */             break label623;
/*     */           }
/*     */           
/*     */ 
/*     */ 
/* 308 */           boolean created = item.getSavedSequence() > begin;
/* 309 */           ToXML.encodeItem(response, ifmt, octxt, item, created ? 4194592 : 4223782);
/* 310 */           itemCount++;
/*     */         } }
/* 312 */       batch.clear();
/*     */     }
/*     */     
/*     */     label623:
/* 316 */     if (changed.getSecond() != null) {
/* 317 */       tombstones.addAll((TypedIdList)changed.getSecond());
/*     */     }
/*     */     
/*     */ 
/* 321 */     if (tombstones.isEmpty()) {
/* 322 */       eDeleted.detach();
/*     */     } else {
/* 324 */       StringBuilder deleted = new StringBuilder();StringBuilder typed = new StringBuilder();
/* 325 */       for (Map.Entry<MailItem.Type, List<TypedIdList.ItemInfo>> entry : tombstones) {
/* 326 */         typed.setLength(0);
/* 327 */         for (TypedIdList.ItemInfo iinfo : (List)entry.getValue()) {
/* 328 */           deleted.append(deleted.length() == 0 ? "" : ",").append(iinfo.getId());
/* 329 */           if (typedDeletes) {
/* 330 */             typed.append(typed.length() == 0 ? "" : ",").append(iinfo.getId());
/*     */           }
/*     */         }
/* 333 */         if (typedDeletes)
/*     */         {
/* 335 */           String eltName = elementNameForType((MailItem.Type)entry.getKey());
/* 336 */           if (eltName != null) {
/* 337 */             eDeleted.addElement(eltName).addAttribute("ids", typed.toString());
/*     */           }
/*     */         }
/*     */       }
/* 341 */       eDeleted.addAttribute("ids", deleted.toString());
/*     */     }
/*     */     
/* 344 */     return newToken;
/*     */   }
/*     */   
/*     */   public static String elementNameForType(MailItem.Type type) {
/* 348 */     switch (type) {
/*     */     case FOLDER: 
/* 350 */       return "folder";
/*     */     case SEARCHFOLDER: 
/* 352 */       return "search";
/*     */     case MOUNTPOINT: 
/* 354 */       return "link";
/*     */     case FLAG: 
/*     */     case TAG: 
/* 357 */       return "tag";
/*     */     case VIRTUAL_CONVERSATION: 
/*     */     case CONVERSATION: 
/* 360 */       return "c";
/*     */     case CHAT: 
/* 362 */       return "chat";
/*     */     case MESSAGE: 
/* 364 */       return "m";
/*     */     case CONTACT: 
/* 366 */       return "cn";
/*     */     case APPOINTMENT: 
/* 368 */       return "appt";
/*     */     case TASK: 
/* 370 */       return "task";
/*     */     case NOTE: 
/* 372 */       return "note";
/*     */     case WIKI: 
/* 374 */       return "w";
/*     */     case DOCUMENT: 
/* 376 */       return "doc";
/*     */     }
/* 378 */     return null;
/*     */   }
/*     */   
/*     */   public static MailItem.Type typeForElementName(String name)
/*     */   {
/* 383 */     if (name.equals("folder"))
/* 384 */       return MailItem.Type.FOLDER;
/* 385 */     if (name.equals("search"))
/* 386 */       return MailItem.Type.SEARCHFOLDER;
/* 387 */     if (name.equals("link"))
/* 388 */       return MailItem.Type.MOUNTPOINT;
/* 389 */     if (name.equals("tag"))
/* 390 */       return MailItem.Type.TAG;
/* 391 */     if (name.equals("c"))
/* 392 */       return MailItem.Type.CONVERSATION;
/* 393 */     if (name.equals("m"))
/* 394 */       return MailItem.Type.MESSAGE;
/* 395 */     if (name.equals("chat"))
/* 396 */       return MailItem.Type.CHAT;
/* 397 */     if (name.equals("cn"))
/* 398 */       return MailItem.Type.CONTACT;
/* 399 */     if (name.equals("appt"))
/* 400 */       return MailItem.Type.APPOINTMENT;
/* 401 */     if (name.equals("task"))
/* 402 */       return MailItem.Type.TASK;
/* 403 */     if (name.equals("note"))
/* 404 */       return MailItem.Type.NOTE;
/* 405 */     if (name.equals("w"))
/* 406 */       return MailItem.Type.WIKI;
/* 407 */     if (name.equals("doc")) {
/* 408 */       return MailItem.Type.DOCUMENT;
/*     */     }
/* 410 */     return MailItem.Type.UNKNOWN;
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/service/mail/Sync.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */